import numpy as np
from typing import List, Dict, Any, Optional
import logging
import uuid
import numpy as np

from backend.database.db import db_manager
from backend.llm import llm_manager
from backend.common.config import config_manager

logger = logging.getLogger('vector_db')


class VectorDBManager:
    """向量数据库管理器（现在是DatabaseManager的简单封装器）"""
    
    _instance = None
    _initialized = False
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(VectorDBManager, cls).__new__(cls)
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        
        # 直接使用db_manager
        self.db_manager = db_manager
        
        self._initialized = True
    
    def initialize(self):
        """初始化向量数据库"""
        logger.info("向量数据库已通过DatabaseManager初始化")
        
        # 获取当前激活的LLM提供商
        self.active_provider = config_manager.llm_providers.active_provider
        logger.info(f"向量数据库管理器使用LLM提供商: {self.active_provider}")
        
        # 尝试获取LLM提供商实例以确保它已初始化
        try:
            self.llm_provider = llm_manager.get_provider(self.active_provider)
            logger.info("成功初始化LLM提供商")
        except Exception as e:
            logger.error(f"初始化LLM提供商失败: {str(e)}")
            self.llm_provider = None

    def create_tables(self):
        """创建向量数据库所需的表"""
        try:
            self.db_manager.create_tables()
            logger.info("向量数据库表创建成功")
        except Exception as e:
            logger.error(f"创建向量数据库表失败: {str(e)}")
    
    def store_vector(self, embedding: np.ndarray, metadata: Dict = None) -> int:
        """存储向量到数据库
        
        Args:
            embedding: 向量数据
            metadata: 向量的元数据
            
        Returns:
            向量ID
        """
        try:
            if self.db_manager.in_memory_mode:
                # 内存模式
                vector_id = self.db_manager.next_vector_id
                self.db_manager.memory_vectors.append({
                    'id': vector_id,
                    'embedding': embedding,
                    'metadata': metadata or {}
                })
                self.db_manager.next_vector_id += 1
                return vector_id
            else:
                # 数据库模式
                content = metadata.get('content', '') if metadata else ''
                doc_metadata = metadata.get('metadata', {}) if metadata else {}
                
                document_id = self.db_manager.insert_document(
                    content=content,
                    metadata=doc_metadata,
                    vector=embedding.tolist()
                )
                
                return document_id
        except Exception as e:
            logger.error(f"存储向量失败: {str(e)}")
            return -1
    
    def batch_store_vectors(self, embeddings: List[np.ndarray], metadatas: List[Dict] = None) -> List[int]:
        """批量存储向量到数据库
        
        Args:
            embeddings: 向量列表
            metadatas: 向量的元数据列表
            
        Returns:
            向量ID列表
        """
        if metadatas is None:
            metadatas = [{} for _ in range(len(embeddings))]
        
        vector_ids = []
        for i, embedding in enumerate(embeddings):
            metadata = metadatas[i] if i < len(metadatas) else {}
            vector_id = self.store_vector(embedding, metadata)
            vector_ids.append(vector_id)
        
        return vector_ids
    
    def search_similar_vectors(self, query_embedding: np.ndarray, top_k: int = 8, 
                              score_threshold: float = 0.5) -> List[Dict]:
        """搜索与查询向量相似的向量
        
        Args:
            query_embedding: 查询向量
            top_k: 返回的最大结果数
            score_threshold: 相似度阈值
            
        Returns:
            相似向量列表
        """
        try:
            if self.db_manager.in_memory_mode:
                # 内存模式下的简单余弦相似度搜索
                results = []
                for vector in self.db_manager.memory_vectors:
                    # 计算余弦相似度
                    sim = np.dot(query_embedding, vector['embedding']) / \
                          (np.linalg.norm(query_embedding) * np.linalg.norm(vector['embedding']))
                    
                    if sim >= score_threshold:
                        results.append({
                            'id': vector['id'],
                            'similarity': sim,
                            'metadata': vector['metadata']
                        })
                
                # 按相似度排序并返回前top_k个结果
                results.sort(key=lambda x: x['similarity'], reverse=True)
                return results[:top_k]
            else:
                # 使用数据库进行向量搜索
                results = self.db_manager.search_documents(
                    query_vector=query_embedding.tolist(), 
                    top_k=top_k, 
                    score_threshold=score_threshold
                )
                
                # 转换结果格式以保持一致
                formatted_results = []
                for result in results:
                    formatted_result = {
                        'id': result['id'],
                        'content': result['content'],
                        'metadata': result['metadata'],
                        'similarity': result['similarity']
                    }
                    formatted_results.append(formatted_result)
                
                return formatted_results
        except Exception as e:
            logger.error(f"向量搜索失败: {str(e)}")
            return []
    
    def delete_vector(self, vector_id: int) -> bool:
        """删除指定ID的向量
        
        Args:
            vector_id: 向量ID
            
        Returns:
            是否删除成功
        """
        return self.db_manager.delete_vector(vector_id)
    
    def count_vectors(self) -> int:
        """获取向量总数
        
        Returns:
            向量数量
        """
        return self.db_manager.count_vectors()
    
    def insert_session_message(self, session_id: str, message_id: str, role: str, 
                          content: str, metadata: Dict = None, rag_context: Dict = None):
        """插入聊天历史记录
        
        Args:
            session_id: 会话ID
            message_id: 消息ID
            role: 角色（user/assistant）
            content: 内容
            metadata: 元数据
            rag_context: RAG上下文
        """
        try:
            # 确保ID是UUID格式
            if not isinstance(session_id, uuid.UUID):
                session_id = uuid.UUID(session_id)
            if not isinstance(message_id, uuid.UUID):
                message_id = uuid.UUID(message_id)
            
            self.db_manager.insert_session_message(
                session_id=session_id,
                message_id=message_id,
                role=role,
                content=content,
                metadata=metadata,
                rag_context=rag_context
            )
            logger.debug(f"插入聊天历史成功: {session_id}, {role}")
        except Exception as e:
            logger.error(f"插入聊天历史失败: {str(e)}")
    
    def get_session_messages(self, session_id: str, limit: int = 20) -> List[Dict]:
        """获取聊天历史记录
        
        Args:
            session_id: 会话ID
            limit: 结果数量限制
            
        Returns:
            聊天历史列表
        """
        try:
            # 确保ID是UUID格式
            if not isinstance(session_id, uuid.UUID):
                session_id = uuid.UUID(session_id)
            
            results = self.db_manager.get_session_messages(session_id, limit)
            return results
        except Exception as e:
            logger.error(f"获取聊天历史失败: {str(e)}")
            return []
    
    def insert_performance_metrics(self, metrics: Dict[str, Any]):
        """插入性能指标
        
        Args:
            metrics: 性能指标数据
        """
        try:
            self.db_manager.insert_performance_metrics(metrics)
            logger.debug(f"插入性能指标成功")
        except Exception as e:
            logger.error(f"插入性能指标失败: {str(e)}")
    
    def generate_embedding(self, text: str) -> np.ndarray:
        """生成文本嵌入向量
        
        Args:
            text: 输入文本
            
        Returns:
            嵌入向量
        """
        try:
            # 确保LLM提供商已初始化
            if not hasattr(self, 'llm_provider') or self.llm_provider is None:
                self.active_provider = config_manager.llm_providers.active_provider
                self.llm_provider = llm_manager.get_provider(self.active_provider)
                
            # 调用LLM提供商生成嵌入
            embedding = self.llm_provider.generate_embedding(text)
            logger.debug(f"成功生成文本嵌入，长度: {len(embedding)}")
            return embedding
        except Exception as e:
            logger.error(f"生成文本嵌入失败: {str(e)}")
            # 返回随机向量作为后备
            return np.random.rand(4096)  # 兼容Ollama的默认嵌入维度
    
    def close(self):
        """关闭向量数据库连接"""
        if hasattr(self.db_manager, 'close'):
            self.db_manager.close()
        logger.info("向量数据库连接已关闭")


# 创建全局向量数据库管理器实例
vector_db_manager = VectorDBManager()

